use axum::{
    extract::FromRequestParts,
    http::{StatusCode, request::Parts},
    response::IntoResponse,
};
use jsonwebtoken as jwd;
use jsonwebtoken::{DecodingKey, Validation};
use serde::{Deserialize, Serialize, de::DeserializeOwned};

pub const SECRET: &[u8] = b"thisisasecret";

// 编码进token中的信息T
#[derive(Serialize, Deserialize, Debug)]
pub struct Claim<T>(pub T);

impl<S, T> FromRequestParts<S> for Claim<T>
where
    S: Send + Sync,
    T: DeserializeOwned + 'static,
{
    type Rejection = BizError;

    async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
        let token = parts.headers.get("Authorization").ok_or(BizError::Auth)?;

        let token = token.to_str().map_err(|_| BizError::Auth)?;
        let token = token.trim_start_matches("Bearer "); // 移除可能的Bearer前缀

        let mut validation = Validation::default();
        validation.validate_exp = false;

        let token = jwd::decode::<T>(token, &DecodingKey::from_secret(SECRET), &validation)
            .map_err(|e| {
                tracing::error!("decode token failed: {}", e);
                BizError::Auth
            })?;

        Ok(Claim(token.claims))
    }
}

#[allow(unused)]
pub enum BizError {
    Auth,
    Internal,
}

impl IntoResponse for BizError {
    fn into_response(self) -> axum::response::Response {
        let (code, msg) = match self {
            BizError::Auth => (StatusCode::UNAUTHORIZED, "no auth"),
            BizError::Internal => (StatusCode::INTERNAL_SERVER_ERROR, "internal error"),
        };
        (code, msg).into_response()
    }
}
